package br.ufrn.dimap.ase.squid.metrics.visitors;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import org.eclipse.emf.common.util.EList;

import br.ufrn.dimap.ase.squid.core.models.squidmodel.Feature;
import br.ufrn.dimap.ase.squid.core.models.squidmodel.Mapping;
import br.ufrn.dimap.ase.squid.core.models.squidmodel.impl.ClassImpl;
import br.ufrn.dimap.ase.squid.core.models.squidmodel.impl.CodePieceImpl;
import br.ufrn.dimap.ase.squid.core.models.squidmodel.impl.FieldImpl;
import br.ufrn.dimap.ase.squid.core.models.squidmodel.impl.MethodImpl;
import br.ufrn.dimap.ase.squid.metrics.tamanho.Contador;
import br.ufrn.dimap.ase.squid.metrics.views.tree.model.InfoNode;
import br.ufrn.dimap.ase.squid.metrics.views.tree.model.Resultado;

public class VisitorGranularidade extends AbstractVisitor {
	private static int NUMERO_METRICAS = 6;
	
	protected Map<String, List<Mapping>> mapping;
	
	private int metricAttribute = 0;
	private int metricMethod = 0;
	private int metricClass = 0;
	private int metricStatement = 0;
	private int metricExpression = 0;
	private int metricClassSignature = 0;
	
	private List<InfoNode> lMethod;
	private List<InfoNode> lAttributes;
	private List<InfoNode> lClass;
	private List<InfoNode> lStatement;
	private List<InfoNode> lExpression;
	private List<InfoNode> lClassSignature;
	
	public VisitorGranularidade(Map<String, List<Mapping>> mapping) {
		this.mapping = mapping;
		
		lMethod = new ArrayList<InfoNode>();
		lAttributes = new ArrayList<InfoNode>();
		lClass = new ArrayList<InfoNode>();
		lStatement = new ArrayList<InfoNode>();
		lExpression = new ArrayList<InfoNode>();
		lClassSignature = new ArrayList<InfoNode>();
	}
	
	public void visit(ClassImpl _class, Mapping mapping, String feature){
		++metricClass;
		this.lClass.add(new InfoNode( getPath(_class) ));
	}

	public void visit(MethodImpl method, Mapping mapping, String feature){
		System.out.println("\t" + method.getName());
		
		//	Verificar se o metodo ta vindo da anotacao de uma classe
		List<Mapping> maps = this.mapping.get(feature);
		for(Mapping map : maps) {
			if(map.getAssets() instanceof ClassImpl) {
				String _class = map.getAssets().getName();
				if(_class.contains(".java"))
					_class = _class.substring(0, _class.indexOf(".java"));
				
				if(_class.equalsIgnoreCase( getPath(method) ) )
					return;
			}
		}

		++metricMethod;
		this.lMethod.add(new InfoNode( getPath(method) ));
	}

	public void visit(FieldImpl field, Mapping mapping, String feature){
		++metricAttribute;
		this.lAttributes.add(new InfoNode( getPath(field) ));
	}
	
	public void visit(CodePieceImpl codePiece, Mapping mapping, String feature){
		this.calcularMetricasExpression(codePiece);
		int classSignatureAntes = metricClassSignature;
		this.calcularMetricasClassSignature(codePiece);

		/**
		 * Se for um ClassSignature, nao conta como metricStatement
		 * Se for um ClassSignature, o valor de metricClassSignature ira mudar no metodo this.calcularMetricasClassSignature(codePiece), 
		 * 	e ira entrar no seguinte if
		 */
		if(classSignatureAntes == metricClassSignature){
			++metricStatement;
			this.lStatement.add(new InfoNode( getPath(codePiece) ));
		}
	}
	
	private void calcularMetricasExpression(CodePieceImpl codePiece){
		Contador contador = new Contador();
		
		/**
		 * TODO
		 * Mudar para expressao regular, para pegar
		 * todos os casos possiveis
		 */
		String str = this.preProcess(codePiece);
		str = str.replace("// #endif", "");
		str = str.replace("//#endif", "");
		str = contador.getRealLine(str);
		if(str.indexOf("||") == 0 || str.indexOf("&&") == 0
				|| str.indexOf("||") == str.length()-2 
				|| str.indexOf("&&") == str.length()-2){
			++metricExpression;
			this.lExpression.add(new InfoNode( getPath(codePiece) ));
		}
	}
	
	private void calcularMetricasClassSignature(CodePieceImpl codePiece){
		String expRegular = "[ ]*[extends]*[ ]*[[a-zA-Z0-0]*[ ]*,]*";
		String str = this.preProcess(codePiece);
		str = str.replace("\n", " ");
		
		if(Pattern.matches(expRegular, str)) {
			this.metricClassSignature++;
			this.lClassSignature.add(new InfoNode( getPath(codePiece) ));
		}
	}
	
	/**
	 * Esse metodo retira os comentarios que comecam no inicio da linha
	 * Por default ja vem uma '/' no inicio de um codePiece
	 * 
	 * @param codePiece
	 * @return
	 */
	private String preProcess(CodePieceImpl codePiece){
		String str = codePiece.getCode().replace("\t", "");
		while(str.indexOf("//") == 0 || str.indexOf("/") == 0 || str.indexOf("/*") == 0) {
			if(str.indexOf("/*") == 0)
				str = str.substring(str.indexOf("*/")+2);
			else
				str = str.substring(str.indexOf("\n")+1);
		}
		return str;
	}

	public String toString(){
		String str = "";	
		
		str += "Metric Attribute: " + metricAttribute + "\n";
		str += "Metric Methods: " + metricMethod + "\n";
		str += "Metric Class: " + metricClass + "\n";
		str += "Metric ClassSignature: " + metricClassSignature + "\n";
		str += "Metric Statement: " + metricStatement + "\n";
		str += "Metric Expression: " + metricExpression + "\n";
		
		return str;
	}
	
	public Resultado[] getResultados() {
		tirarRepeticoesListas(lAttributes, lMethod, lClass, lClassSignature, lStatement, lExpression);
		
		Resultado[] resultados = new Resultado[NUMERO_METRICAS];
		int n = 0;
		
		resultados[n++] = new Resultado(
				"Metric Attribute: " + metricAttribute,
				this.lAttributes
			);
		resultados[n++] = new Resultado(
				"Metric Methods: " + metricMethod,
				this.lMethod
			);
		resultados[n++] = new Resultado(
				"Metric Class: " + metricClass,
				this.lClass
			);
		resultados[n++] = new Resultado(
				"Metric ClassSignature: " + metricClassSignature,
				this.lClassSignature
			);
		resultados[n++] = new Resultado(
				"Metric Statement: " + metricStatement,
				this.lStatement
			);
		resultados[n++] = new Resultado(
				"Metric Expression: " + metricExpression,
				this.lExpression
			);
		
		return resultados;
	}
	
	public static void main(String a[]){
//		System.out.println(Pattern.matches("[[a-zA-Z0-0]*[ ]*,]*", str));
	}
}
